﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Diagnostics;

namespace DiscreteEventModel.Common {

    public class MemoryMappedDictionary<TKey, TItem> : IDictionary<TKey, TItem>
        where TKey : struct
        where TItem : IBinarySerializable {

        private ObjectFile<TKey> _objectFile;
        private Func<TItem> _itemFactory;

        public MemoryMappedDictionary(DirectoryInfo workingDirectory, Guid id, int pageCapacity, long maxPageCount, Func<TItem> itemFactory) {
            this._objectFile = new ObjectFile<TKey>(workingDirectory, id, pageCapacity, maxPageCount);
            this._itemFactory = itemFactory;
        }

        private IEnumerable<KeyValuePair<TKey, TItem>> Entries {
            get {
                foreach(TKey key in this._objectFile.Tokens) {
                    TItem item;
                    if(this.TryGetValue(key, out item)) {
                        yield return new KeyValuePair<TKey, TItem>(key, item);
                    }
                }
            }
        }

        #region IDictionary<TKey,TItem> Members

        public void Add(TKey key, TItem value) {
            using(IBinaryWriter writer = this._objectFile.Write(key)) {
                value.Serialize(writer);
            }
        }

        public bool ContainsKey(TKey key) {
            return this._objectFile.Tokens.Contains(key);
        }

        public ICollection<TKey> Keys {
            get { return this._objectFile.Tokens; }
        }

        public bool Remove(TKey key) {
            return this._objectFile.Remove(key);
        }

        public bool TryGetValue(TKey key, out TItem value) {
            IBinaryReader reader = this._objectFile.Read(key);
            if(reader == null) {
                value = default(TItem);
                return false;
            }

            value = this._itemFactory();
            value.Deserialize(reader);
            reader.Dispose();
            return true;
        }

        public ICollection<TItem> Values {
            get { throw new NotImplementedException(); }
        }

        public TItem this[TKey key] {
            get {
                TItem item;
                if(!this.TryGetValue(key, out item)) {
                    throw new Exception();
                }
                return item;
            }
            set {
                if(value == null) {
                    this._objectFile.Remove(key);
                } else {
                    using(IBinaryWriter writer = this._objectFile.Write(key)) {
                        value.Serialize(writer);
                    }
                }
            }
        }

        #endregion

        #region ICollection<KeyValuePair<TKey,TItem>> Members

        public void Add(KeyValuePair<TKey, TItem> item) {
            this.Add(item.Key, item.Value);
        }

        public void Clear() {
            foreach(TKey key in new List<TKey>(this._objectFile.Tokens)) {
                this._objectFile.Remove(key);
            }
        }

        public bool Contains(KeyValuePair<TKey, TItem> entry) {
            TItem item;
            return this.TryGetValue(entry.Key, out item) && entry.Equals(item);
        }

        public void CopyTo(KeyValuePair<TKey, TItem>[] array, int arrayIndex) {
            foreach(KeyValuePair<TKey, TItem> entry in this.Entries) {
                array[arrayIndex++] = entry;
            }
        }

        public int Count {
            get { return this._objectFile.Tokens.Count; }
        }

        public bool IsReadOnly {
            get { return false; }
        }

        public bool Remove(KeyValuePair<TKey, TItem> item) {
            return this.Remove(item.Key);
        }

        #endregion

        #region IEnumerable<KeyValuePair<TKey,TItem>> Members

        public IEnumerator<KeyValuePair<TKey, TItem>> GetEnumerator() {
            return this.Entries.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
            return this.Entries.GetEnumerator();
        }

        #endregion
    }
}
